/**
 * Adds 1 public method to MCG and 1 to L.Marker to facilitate changing
 * markers' icon options and refreshing their icon and their parent clusters
 * accordingly (case where their iconCreateFunction uses data of childMarkers
 * to make up the cluster icon).
 */


L.MarkerClusterGroup.include({
    /**
     * Updates the icon of all clusters which are parents of the given marker(s).
     * In singleMarkerMode, also updates the given marker(s) icon.
     * @param layers L.MarkerClusterGroup|L.LayerGroup|Array(L.Marker)|Map(L.Marker)|
     * L.MarkerCluster|L.Marker (optional) list of markers (or single marker) whose parent
     * clusters need to be updated. If not provided, retrieves all child markers of this.
     * @returns {L.MarkerClusterGroup}
     */
    refreshClusters: function (layers) {
        if (!layers) {
            layers = this._topClusterLevel.getAllChildMarkers();
        } else if (layers instanceof L.MarkerClusterGroup) {
            layers = layers._topClusterLevel.getAllChildMarkers();
        } else if (layers instanceof L.LayerGroup) {
            layers = layers._layers;
        } else if (layers instanceof L.MarkerCluster) {
            layers = layers.getAllChildMarkers();
        } else if (layers instanceof L.Marker) {
            layers = [layers];
        } // else: must be an Array(L.Marker)|Map(L.Marker)
        this._flagParentsIconsNeedUpdate(layers);
        this._refreshClustersIcons();

        // In case of singleMarkerMode, also re-draw the markers.
        if (this.options.singleMarkerMode) {
            this._refreshSingleMarkerModeMarkers(layers);
        }

        return this;
    },

    /**
     * Simply flags all parent clusters of the given markers as having a "dirty" icon.
     * @param layers Array(L.Marker)|Map(L.Marker) list of markers.
     * @private
     */
    _flagParentsIconsNeedUpdate: function (layers) {
        var id, parent;

        // Assumes layers is an Array or an Object whose prototype is non-enumerable.
        for (id in layers) {
            // Flag parent clusters' icon as "dirty", all the way up.
            // Dumb process that flags multiple times upper parents, but still
            // much more efficient than trying to be smart and make short lists,
            // at least in the case of a hierarchy following a power law:
            // http://jsperf.com/flag-nodes-in-power-hierarchy/2
            parent = layers[id].__parent;
            while (parent) {
                parent._iconNeedsUpdate = true;
                parent = parent.__parent;
            }
        }
    },

    /**
     * Re-draws the icon of the supplied markers.
     * To be used in singleMarkerMode only.
     * @param layers Array(L.Marker)|Map(L.Marker) list of markers.
     * @private
     */
    _refreshSingleMarkerModeMarkers: function (layers) {
        var id, layer;

        for (id in layers) {
            layer = layers[id];

            // Make sure we do not override markers that do not belong to THIS group.
            if (this.hasLayer(layer)) {
                // Need to re-create the icon first, then re-draw the marker.
                layer.setIcon(this._overrideMarkerIcon(layer));
            }
        }
    }
});

L.Marker.include({
    /**
     * Updates the given options in the marker's icon and refreshes the marker.
     * @param options map object of icon options.
     * @param directlyRefreshClusters boolean (optional) true to trigger
     * MCG.refreshClustersOf() right away with this single marker.
     * @returns {L.Marker}
     */
    refreshIconOptions: function (options, directlyRefreshClusters) {
        var icon = this.options.icon;

        L.setOptions(icon, options);

        this.setIcon(icon);

        // Shortcut to refresh the associated MCG clusters right away.
        // To be used when refreshing a single marker.
        // Otherwise, better use MCG.refreshClusters() once at the end with
        // the list of modified markers.
        if (directlyRefreshClusters && this.__parent) {
            this.__parent._group.refreshClusters(this);
        }

        return this;
    }
});
